<template>
  <div class="app-container">
    <!-- 头部搜索 -->
    <el-row style="display: flex; align-items: center; margin: 20px">
      <el-col :span="12">
        <div>
          <div>
            <el-input placeholder="请输入" v-model="inputValue" class="input-with-select" clearable>
              <el-select v-model="select" slot="prepend" placeholder="请选择">
                <el-option label="IP" value="1"></el-option>
                <el-option label="组织" value="2"></el-option>
              </el-select>
              <el-button slot="append" class="inBtn" @click="search">检索</el-button>
            </el-input>
          </div>
        </div>
      </el-col>
      <el-col :span="4" :offset="8">
        <b> {{ nodeTotal }}个节点,{{ linkTotal }}条边 </b>
      </el-col>
    </el-row>
    <div class="graph">
      <div ref="container" class="graph-container">
        <div class="graph-detail">
          <el-descriptions v-if="currentNode" class="margin-top" :column="1" border>
            <el-descriptions-item>
              <template slot="label"> IP </template>
              {{ currentNode.params.ip }}
            </el-descriptions-item>
            <el-descriptions-item v-if="currentNode.params.org">
              <template slot="label"> 组织 </template>
              {{ currentNode.params.org.name }}
            </el-descriptions-item>
            <el-descriptions-item v-if="currentNode.params.city">
              <template slot="label"> 城市 </template>
              {{ currentNode.params.city.name }}
            </el-descriptions-item>
            <el-descriptions-item v-if="currentNode.params.domain">
              <template slot="label"> 域名 </template>
              {{ currentNode.params.domain.name }}
            </el-descriptions-item>
            <el-descriptions-item v-if="currentNode.params.ipc">
              <template slot="label"> 网段 </template>
              <span v-for="item in currentNode.params.ipc">{{ item.name }}</span>
            </el-descriptions-item>
          </el-descriptions>
          <el-descriptions v-if="currentedges" class="margin-top" :column="1" border>
            <el-descriptions-item>
              <template slot="label"> 起点 </template>
              {{ currentedges.params.from.address }}
            </el-descriptions-item>
            <el-descriptions-item>
              <template slot="label"> 末端 </template>
              {{ currentedges.params.to.name }}
            </el-descriptions-item>
          </el-descriptions>
        </div>
      </div>
    </div>
    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
      @pagination="search" />
  </div>
</template>

<script>
import G6 from "@antv/g6";
import {
  antiHomingInfoList,
  predictAntiHomingInfo,
} from "@/api/IPOwnershipProtection/dataStatistics.js";
export default {
  data() {
    return {
      // 查询参数
      queryParams: {
        pageNum: 0,
        pageSize: 10,
      },
      select: "1",
      nodeTotal: 0,
      linkTotal: 0,
      inputValue: "",
      graph: null,
      graphData: {
        nodes: [],
        edges: [],
      },
      currentNode: null,
      total: 0,
      currentedges: null,
    };
  },
  watch: {
    select: {
      deep: true,
      handler(val) {
        this.inputValue = ''
        this.queryParams = { pageNum: 0, pageSize: 10 }
      },
    },
  },
  created() { },
  mounted() {
    antiHomingInfoList(this.queryParams).then((res) => {
      let nodes = [];
      let edges = [];
      this.total = res.data.totalElements
      res.data.content.map((ele) => {
        nodes.push({
          id: String(ele.address),
          label: ele.address,
          params: { ip: ele.address, type: "", flag: true, city: ele.city, domain: ele.domain, org: ele.org, ipc: ele.ipc },
        });
        if (ele?.city?.name) {
          nodes.push({
            id: String(ele?.city?.name),
            label: ele?.city?.name,
            params: { ip: ele.address, type: "city" },
          });
        }
        if (ele?.domain?.name) {
          nodes.push({
            id: String(ele?.domain?.name),
            label: ele?.domain?.name,
            params: { ip: ele.address, type: "domain" },
          });
        }
        if (ele?.org?.name) {
          nodes.push({
            id: String(ele?.org?.name),
            label: ele?.org?.name,
            params: { ip: ele.address, type: "org" },
          });
        }
        if (ele.ipc) {
          ele.ipc.map((aa) => {
            if (aa?.name) {
              nodes.push({
                id: String(aa.name),
                label: aa.name,
                params: { ip: ele.address, type: "ipc" },
              });
            }
          });
        }
        if (ele.city?.name) {
          edges.push({
            source: String(ele.address),
            target: String(ele.city?.name),
            label: "城市",
            params: { from: ele, to: ele.city },
          });
        }
        if (ele.domain?.name) {
          edges.push({
            source: String(ele.address),
            target: String(ele.domain?.name),
            label: "域名",
            params: { from: ele, to: ele.domain },
          });
        }
        if (ele.org?.name) {
          edges.push({
            source: String(ele.address),
            target: String(ele.org?.name),
            label: "组织",
            params: { from: ele, to: ele.org },
          });
        }
        if (ele.ipc) {
          ele.ipc.map((ee, i) => {
            if (ee?.name) {
              edges.push({
                source: String(ele.address),
                target: String(ee.name),
                label: "网段",
                params: { from: ele, to: ele.ipc[i] },
              });
            }
          });
        }
      });
      const nodesIds = new Set();
      this.graphData.nodes = nodes.filter((item) => {
        if (nodesIds.has(item.id)) {
          return false;
        }
        nodesIds.add(item.id);
        return true;
      });
      this.graphData.edges = edges;
      this.nodeTotal = this.graphData.nodes.length;
      this.linkTotal = this.graphData.edges.length;
      this.initGraph();
    });
  },
  beforeUnmount() {
    if (this.graph) {
      this.graph.destroy();
    }
  },
  methods: {
    search() {
      this.currentNode = null
      this.currentedges = null
      if (this.select == "1") {
        this.queryParams.ip = this.inputValue;
      } else {
        this.queryParams.org = this.inputValue;
      }
      this.graph.clear();
      antiHomingInfoList(this.queryParams).then((res) => {
        let nodes = [];
        let edges = [];
        this.total = res.data.totalElements
        res.data.content.map((ele) => {
          nodes.push({
            id: String(ele.address),
            label: ele.address,
            params: { ip: ele.address, type: "", flag: true, city: ele.city, domain: ele.domain, org: ele.org, ipc: ele.ipc },
          });
          if (ele?.city?.name) {
            nodes.push({
              id: String(ele?.city?.name),
              label: ele?.city?.name,
              params: { ip: ele.address, type: "city" },
            });
          }
          if (ele?.domain?.name) {
            nodes.push({
              id: String(ele?.domain?.name),
              label: ele?.domain?.name,
              params: { ip: ele.address, type: "domain" },
            });
          }
          if (ele?.org?.name) {
            nodes.push({
              id: String(ele?.org?.name),
              label: ele?.org?.name,
              params: { ip: ele.address, type: "org" },
            });
          }
          if (ele.ipc) {
            ele.ipc.map((aa) => {
              if (aa?.name) {
                nodes.push({
                  id: String(aa.name),
                  label: aa.name,
                  params: { ip: ele.address, type: "ipc" },
                });
              }
            });
          }
          if (ele.city?.name) {
            edges.push({
              source: String(ele.address),
              target: String(ele.city?.name),
              label: "城市",
            });
          }
          if (ele.domain?.name) {
            edges.push({
              source: String(ele.address),
              target: String(ele.domain?.name),
              label: "域名",
            });
          }
          if (ele.org?.name) {
            edges.push({
              source: String(ele.address),
              target: String(ele.org?.name),
              label: "组织",
            });
          }
          if (ele.ipc) {
            ele.ipc.map((ee) => {
              if (ee?.name) {
                edges.push({
                  source: String(ele.address),
                  target: String(ee.name),
                  label: "网段",
                });
              }
            });
          }
        });
        const nodesIds = new Set();
        this.graphData.nodes = nodes.filter((item) => {
          if (nodesIds.has(item.id)) {
            return false;
          }
          nodesIds.add(item.id);
          return true;
        });
        this.graphData.edges = edges;
        this.nodeTotal = this.graphData.nodes.length;
        this.linkTotal = this.graphData.edges.length;
        this.graph.data(this.graphData);
        this.graph.render();
        this.graph.getNodes().forEach(node => {
          const model = node.getModel();
          if (model.params.flag) {
            this.graph.updateItem(node, {
              size: 50,
              style: {
                fill: "orange",
                stroke: "#5B8FF9",
              },
            });
          }
        });
      });
    },
    initGraph() {
      const containerWidth = this.$refs.container.offsetWidth;
      const containerHeight = document.documentElement.offsetHeight;
      // 创建关系图实例
      this.graph = new G6.Graph({
        container: this.$refs.container,
        width: containerWidth,
        height: containerHeight - 200,
        modes: {
          default: ["drag-canvas", "zoom-canvas", "drag-node", "activate-relations"],
        },
        defaultNode: {
          type: "circle",
          size: 40,
          style: {
            fill: "#9EC9FF",
            stroke: "#5B8FF9",
            lineWidth: 2,
          },
          labelCfg: {
            style: {
              fill: "#333",
            },
            // // 设置标签位置为节点下方
            // position: "bottom",
            // // 微调标签位置，向下偏移 5 个单位
            // offset: 5,
          },
        },
        defaultEdge: {
          type: "line",
          style: {
            stroke: "#F6BD16",
            lineWidth: 2,
            // endArrow: {
            //   path: G6.Arrow.vee(5, 20, 15), // 使用内置箭头路径，参数分别为箭头宽度、高度、偏移量
            //   d: 15 // 箭头与边末端的距离
            // }
          },
          labelCfg: {
            autoRotate: true,
            style: {
              fill: "#666",
              background: {
                fill: "#fff",
                stroke: "#fff",
                padding: [2, 4, 2, 4],
              },
            },
          },
        },
        layout: {
          type: "force",
          preventOverlap: true,
          linkDistance: 200,
          nodeSpacing: 20,
        },
        // 定义节点和边的状态样式
        nodeStateStyles: {
          hover: {
            cursor: "pointer",
          },
        },
        edgeStateStyles: {
          hover: {
            cursor: "pointer",
          },
        },
      });

      // 监听节点的鼠标进入和离开事件
      this.graph.on("node:mouseenter", (e) => {
        const node = e.item;
        node.setState("hover", true);
        this.graph.paint();
      });
      this.graph.on("node:mouseleave", (e) => {
        const node = e.item;
        node.setState("hover", false);
        this.graph.paint();
      });

      // 监听边的鼠标进入和离开事件
      this.graph.on("edge:mouseenter", (e) => {
        const edge = e.item;
        edge.setState("hover", true);
        this.graph.paint();
      });
      this.graph.on("edge:mouseleave", (e) => {
        const edge = e.item;
        edge.setState("hover", false);
        this.graph.paint();
      });

      // 绑定事件
      this.graph.on("node:click", (e) => {
        this.currentedges = null
        this.clearHighlightEdge()
        const node = e.item;
        if (node.getModel().params.flag) {
          this.$emit("node-click", node.getModel());
          this.highlightNode(node);
          if (this.currentNode?.id == node.getModel().id) {
            this.currentNode = null
            this.cleaHighlightNode()
          } else {
            this.currentNode = node.getModel();
          }
        } else {
          this.cleaHighlightNode()
          this.currentNode = null
        }
      });

      this.graph.on("edge:click", (e) => {
        this.currentNode = null
        this.cleaHighlightNode()
        const edge = e.item;
        this.$emit("edge-click", edge.getModel());
        this.highlightEdge(edge);
        if (this.currentedges?.id == edge.getModel().id) {
          this.currentedges = null
          this.clearHighlightEdge()
        } else {
          this.currentedges = edge.getModel()
          // 高亮边两边的节点
          const sourceNode = edge.getSource(); // 获取边的源节点
          const targetNode = edge.getTarget(); // 获取边的目标节点
          // 高亮源节点和目标节点
          this.graph.setItemState(sourceNode, "selected", true);
          this.graph.setItemState(targetNode, "selected", true);
          this.graph.setItemState(edge, "selected", true);
          // 清除其他节点和边的高亮状态
          this.graph.getNodes().forEach(node => {
            if (node !== sourceNode && node !== targetNode) {
              this.graph.clearItemStates(node, "selected");
            }
          });
          this.graph.getEdges().forEach(edge1 => {
            if (edge1 !== edge) {
              this.graph.clearItemStates(edge1, "selected");
            }
          });
        }
      });
      // 渲染数据
      this.graph.data(this.graphData);
      this.graph.render();
      this.graph.getNodes().forEach(node => {
        const model = node.getModel();
        if (model.params.flag) {
          this.graph.updateItem(node, {
            size: 50,
            style: {
              fill: "orange",
              stroke: "#5B8FF9",
            },
          });
        }
      });
    },

    highlightNode(node) {
      this.cleaHighlightNode()
      node.setState("selected", true);
      this.graph.paint();
    },

    highlightEdge(edge) {
      this.clearHighlightEdge()
      edge.setState("selected", true);
      this.graph.paint();
    },
    cleaHighlightNode() {
      // 清除所有节点状态
      this.graph.getNodes().forEach((n) => {
        n.clearStates(["selected"]);
      });
    },
    clearHighlightEdge() {
      // 清除所有边状态
      this.graph.getEdges().forEach((e) => {
        e.clearStates(["selected"]);
      });
    },
  },
};
</script>

<style lang="scss" scoped>
::v-deep.el-select .el-input {
  width: 130px;
}

::v-deep.input-with-select .el-input-group__prepend {
  background-color: #fff;
}

.inBtn {
  color: white !important;
  width: 106px;
  background: linear-gradient(270deg, #2dbffd 0%, #0e5fff 100%) !important;
}

.graph-container {
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  margin: 20px;
  position: relative;
}

.graph {
  position: relative;

  .graph-detail {
    width: 300px;
    position: absolute;
    right: 20px;
    top: 100px;
  }
}

::v-deep .pagination-container {
  left: 0;
}
</style>